
'
' An Ini Parser class to handle INI files
'
' Author HongShe Liang <starofrainnight@gmail.com>
' @date 2011-10-08

'If Not INIPARSER_VBS_INCLUDED Then 'header grard, avoid duplicate included !
'Public INIPARSER_VBS_INCLUDED 
'INIPARSER_VBS_INCLUDED = TRUE

Class IniParser_t
	Private m_Fso
	' vbObject : a Dirctionary for key, value pairs
	' vbInteger : comment
	' vbString : key, value
	' vbNull : be deleted
	Private m_SectionDictionary
	
	Private Sub Class_Initialize
		Set m_Fso = CreateObject("Scripting.FileSystemObject")
		Set m_SectionDictionary = CreateObject("Scripting.Dictionary")
	End Sub
	
	Private Sub Class_Terminate
	End Sub
	
	' load a specific ini file 
	'
	' @param FileName the specific ini file path you want to load
	Public Sub Load( FilePath )
		Const ForReading = 1
		Dim IniFile 

		' clear dictionary 
		Set m_SectionDictionary = CreateObject("Scripting.Dictionary")
		
		' avoid OpenTextFile exceptions
		Set IniFile = m_Fso.OpenTextFile( m_Fso.GetAbsolutePathName(FilePath), ForReading )		
		On Error Goto 0
		
		If IsNull(IniFile) Then
			Set IniFile = m_Fso.CreateTextFile( m_Fso.GetAbsolutePathName(FilePath) )
			If IsNull(IniFile) Then
				' if we can not open the specific file and can not create one, we 
				' must abort current process.
				Exit Sub
			End If
		End If
		
		' ok, next we have a valid ini file obejct, we first load all data from
		' it.
		Dim SectionRe
		Dim KeyValuePairRe
		Dim Matches
		Dim ALine 
		Dim RetStr
		Dim LastSection 
		Dim LastSectionDictionary
		' we do not have a valid method to check the result set of RegExp if empty, so
		' we check it in a for loop, god bless me !
		Dim IsMatchesHasMembers
		Dim LineCount 
		
		Set SectionRe = New RegExp
		Set KeyValuePairRe = New RegExp
		
		SectionRe.Pattern = "^\s*\[(.*)\]\s*$"
		KeyValuePairRe.Pattern = "^\s*([a-zA-Z0-9 _-]+)\s*=\s*([a-zA-Z0-9 _-]+(.)+)\s*$"	
		
		LastSection = ""
		Set LastSectionDictionary = m_SectionDictionary
		
		LineCount = 0
		While Not IniFile.AtEndOfStream
			ALine = IniFile.ReadLine()
      		
      		' detect sections
      		Set Matches = SectionRe.Execute( ALine )
      		IsMatchesHasMembers = False
      		For Each Match In Matches
      			IsMatchesHasMembers = True
      			Exit For
      		Next
      		If IsMatchesHasMembers Then
      			' ensure there have this dictionary, if it do not have, we create one !
      			LastSection = Trim( Matches(0).SubMatches(0) )
      			
      			If Not m_SectionDictionary.Exists( LastSection ) Then
      				m_SectionDictionary.Add LastSection, CreateObject("Scripting.Dictionary") 
      			End If

      			Set LastSectionDictionary = m_SectionDictionary.Item(LastSection)
      		Else      		
				' detect key / value pairs
				Set Matches = KeyValuePairRe.Execute( ALine )
				IsMatchesHasMembers = False
				For Each Match In Matches
					IsMatchesHasMembers = True
					Exit For
				Next
				If IsMatchesHasMembers Then
					LastSectionDictionary.Add Trim(Matches(0).SubMatches(0)), Trim(Matches(0).SubMatches(1))
				Else				
					' detect comment
					LastSectionDictionary.Add LineCount & "@" & ALine, 0
				End If 
			End If
     		
			LineCount = LineCount + 1
      	Wend
	End Sub 
	
	' save ini file back to file
	Public Sub Save( FilePath )
		Dim IniFile 
		Dim i
		Dim j
		Dim Items
		Dim Keys
		Dim SectionItems
		Dim SectionKeys
		
		Set IniFile = m_Fso.CreateTextFile( m_Fso.GetAbsolutePathName(FilePath), True )
		If IsNull(IniFile) Then
			' if we can not open the specific file and can not create one, we 
			' must abort current process.
			Exit Sub
		End If
		
		Items = m_SectionDictionary.Items
		Keys = m_SectionDictionary.Keys
		For i = 0 To m_SectionDictionary.Count - 1
			If vbInteger = VarType( Items(i) ) Then
				' write comment 
				IniFile.WriteLine( Right( Keys(i), ( Len( Keys(i) ) - InStr( 1, Keys(i), "@", vbBinaryCompare ) ) ) )
			ElseIf vbObject = VarType( Items(i) ) Then
				' write section objects
				IniFile.WriteLine( "[" & Keys(i) & "]" )
				
				SectionItems = Items(i).Items
				SectionKeys = Items(i).Keys
				
				For j = 0 To Items(i).Count - 1 				
					If vbInteger = VarType( SectionItems(j) ) Then			
						' write comment 
						IniFile.WriteLine( Right( SectionKeys(j), ( Len( SectionKeys(j) ) - InStr( 1, SectionKeys(j), "@", 0 ) ) ) )
					ElseIf vbNull = VarType( SectionItems(j) ) Then
						' if the value is Null, we do nothing, because, the Null value
						' use to indicate we deleted that value already
					Else
						' write key and values						
						IniFile.WriteLine( SectionKeys(j) & " = " & SectionItems(j) )
					End If	
				Next
			ElseIf vbNull = VarType( Items(i) ) Then
				' if the value is Null, we do nothing, because, the Null value
				' use to indicate we deleted that value already
			Else
				' write key and values
				IniFile.WriteLine( Keys(i) & " = " & Items(i) )
			End If				
		Next
	End Sub
	
	Public Function GetValue( Section, Key, DefaultValue )
		GetValue = DefaultValue
		If m_SectionDictionary.Exists( Section ) Then
			If	m_SectionDictionary.Item( Section ).Exists( Key ) Then
				GetValue = m_SectionDictionary.Item( Section ).Item( Key )
			End If	
		End If
	End Function
	
	Public Function SetValue( Section, Key, Value )
		Dim ADictionary		
		If m_SectionDictionary.Exists( Section ) Then		
			Set ADictionary = m_SectionDictionary.Item( Section )
		Else
			Set ADictionary = CreateObject( "Scripting.Dictionary" )
			Set	m_SectionDictionary.Item( Section ) = ADictionary		
		End If
		
		ADictionary.Item( Key ) = Value
	End Function
	
End Class


'End If 'INIPARSER_VBS_INCLUDED